package com.oauth.server.config;

import com.oauth.server.extend.ex.CustomWebResponseExceptionTranslator;
import com.oauth.server.extend.gant.SmsTokenGranter;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * @作者: 林江
 * @创建时间: 2024/1/10
 * @功能: 授权服务器
 */
@Configuration
@EnableAuthorizationServer // 必须开启
public class SecurityOauth2Config extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()"); // token的访问的url，默认是拒绝的。
        security.checkTokenAccess("permitAll()"); // token的校验的url，默认也是拒绝的。
        security.allowFormAuthenticationForClients(); // 允许客户端进行表单验证
        /*----------------其他用途---------------------------------*/
//        security.passwordEncoder(new BCryptPasswordEncoder());//配置解密码器
//        security.authenticationEntryPoint();//认证失败回调
//        security.accessDeniedHandler();//授权失败回调
//        security.realm(); // ?
//        security.sslOnly();//配置仅支持https
//
//        //自定义客户端认证过滤器
//        security.addTokenEndpointAuthenticationFilter();
//        security.tokenEndpointAuthenticationFilters();
    }

    /**
     * 配置客户端 -- 基于内存的配置
     *
     * @param clients the client details configurer
     * @throws Exception
     */
//    @Override
//    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        clients.inMemory()
//                .withClient("01-client")
//                .secret("{noop}secret")
//                .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit", "client_credentials")
//                .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
//                .scopes("read", "write", "trust")
//                .resourceIds("order-resource") // 订单客户端
//                .redirectUris("http://www.baidu.com")
//                .accessTokenValiditySeconds(600)
//                .and()
//
//                .withClient("02-client")
//                .secret("{noop}123")
//                .authorizedGrantTypes("authorization_code")
//                .authorities("ROLE_CLIENT")
//                .scopes("read", "trust")
//                .resourceIds("product-resource") // 商品客户端
//                .redirectUris("")
//                .and()
//
//                .withClient("03-client")
//                .authorizedGrantTypes("client_credentials", "password")
//                .authorities("ROLE_CLIENT")
//                .scopes("read")
//                .resourceIds("pay-resource") // 支付客户端
//                .secret("{noop}secret")
//                .and()
//        ;
//    }

    /**
     * 配置客户端 -- 基于数据库的配置
     *
     * @param clients the client details configurer
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource());
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new HikariDataSource();
    }

    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    AuthenticationManagerBuilder authenticationManagerBuilder; //rbdcUserDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 密码模式下，必须要借助security本身的认证管理器的支持。如下配置
        endpoints
                .authenticationManager(authenticationManager)
                .userDetailsService(authenticationManagerBuilder.getDefaultUserDetailsService());

        /*---------------其他用途------------------*/
//        endpoints.prefix();
//        endpoints.pathMapping();
//        endpoints.addInterceptor();//添加拦截器
//        允许请求token端点的请求方式，默认是post请求。
//        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
//
//        endpoints.accessTokenConverter();//token装换器
//        endpoints.setClientDetailsService();//自定义客户端查询。一般都是查询数据库
//        endpoints.approvalStore();//将用户选择的授权批准存储起来
//        endpoints.approvalStoreDisabled();//禁用
        endpoints.exceptionTranslator(new CustomWebResponseExceptionTranslator());//oauth2中的异常处理
//        endpoints.redirectResolver();//对配置的客户端中重定向地址的解析器
//        endpoints.reuseRefreshTokens();//是否重用refresh_token
        endpoints.tokenGranter(getCustomTokenGranter(endpoints));//token授予者
        endpoints.tokenEnhancer(getCustomTokenEnhancer());//对accessToken进行增强
//        //授权码服务，如生成授权码，保存授权码等
//        endpoints.authorizationCodeServices();
//        endpoints.tokenServices();
//
        endpoints.tokenStore(new JdbcTokenStore(dataSource()));// token的存储
//        endpoints.userApprovalHandler();//用户审批结果的处理器


    }

    /**
     * access_token的增强处理
     * 1.链式
     * 2.直接
     *
     * @return
     */
    private TokenEnhancer getCustomTokenEnhancer() {
        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();

        // 内置的jwt类型的access_token
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        tokenEnhancers.add(converter);

        TokenEnhancerChain chain = new TokenEnhancerChain();
        chain.setTokenEnhancers(tokenEnhancers);
        return chain;
    }

    /**
     * 获取自定义的授权码
     *
     * @return
     */
    private TokenGranter getCustomTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
        List<TokenGranter> tokenGranters = new ArrayList<>();
        // 默认的4中授权码类型
        tokenGranters.add(endpoints.getTokenGranter());
        // 自定义的授权码类型
        // 短信
        SmsTokenGranter smsTokenGranter = new SmsTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());
        tokenGranters.add(smsTokenGranter);

        return new CompositeTokenGranter(tokenGranters);
    }
}
